package com.xyazm.fulfill.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.xyazm.common.constants.RedisLockKeyConstants;
import com.xyazm.common.enums.OrderStatusChangeEnum;
import com.xyazm.common.enums.OrderTypeEnum;
import com.xyazm.common.redis.RedisLock;
import com.xyazm.common.utils.RandomUtil;
import com.xyazm.fulfill.dao.OrderFulfillDAO;
import com.xyazm.fulfill.dao.OrderFulfillItemDAO;
import com.xyazm.fulfill.dao.OrderFulfillLogDAO;
import com.xyazm.fulfill.domain.builder.FulfillData;
import com.xyazm.fulfill.domain.entity.OrderFulfillDO;
import com.xyazm.fulfill.domain.entity.OrderFulfillItemDO;
import com.xyazm.fulfill.domain.entity.OrderFulfillLogDO;
import com.xyazm.fulfill.domain.param.CancelFulfillParam;
import com.xyazm.fulfill.domain.param.TriggerFulFillItemParam;
import com.xyazm.fulfill.domain.param.TriggerFulFillParam;
import com.xyazm.fulfill.domain.param.TriggerOrderAfterFulfillEventParam;
import com.xyazm.fulfill.enums.OrderFulfillOperateTypeEnum;
import com.xyazm.fulfill.enums.OrderFulfillStatusEnum;
import com.xyazm.fulfill.enums.OrderFulfillTypeEnum;
import com.xyazm.fulfill.event.OrderAfterFulfillEventProcessor;
import com.xyazm.fulfill.event.WmsShipEventProcessorFactory;
import com.xyazm.fulfill.exception.FulfillBizException;
import com.xyazm.fulfill.exception.FulfillErrorCodeEnum;
import com.xyazm.fulfill.mapstruct.FulfillApiMapstruct;
import com.xyazm.fulfill.service.IFulfillScheduleService;
import com.xyazm.fulfill.service.IFulfillService;
import com.xyazm.fulfill.utils.OrderFulfillOperateLogUtil;
import com.xyazm.product.enums.ProductTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class FulfillServiceImpl implements IFulfillService {

    @Autowired
    private OrderFulfillDAO orderFulfillDAO;
    @Autowired
    private OrderFulfillItemDAO orderFulfillItemDAO;
    @Autowired
    private OrderFulfillLogDAO orderFulfillLogDAO;
    @Autowired
    private IFulfillScheduleService fulfillScheduleService;
    @Autowired
    private WmsShipEventProcessorFactory wmsShipEventProcessorFactory;
    @Autowired
    private RedisLock redisLock;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void triggerFulFill(TriggerFulFillParam param) {
        log.info("触发订单履约成功，param:{}", JSONObject.toJSONString(param));
        String orderId = param.getOrderId();
        // 加分布式锁（防止重复触发履约）
        String key = RedisLockKeyConstants.FULFILL_KEY + orderId;
        boolean lock = redisLock.tryLock(key);
        if (!lock) {
            throw new FulfillBizException(FulfillErrorCodeEnum.ORDER_FULFILL_ERROR);
        }
        try {
            // 1.幂等：校验orderId是否已经履约过
            if(orderFulfilled(orderId)) {
                log.info("该订单已履约！！！,orderId={}",orderId);
                return;
            }
            // 2.创建履约单
            FulfillData fulfillData = createFulfillOrders(param);

            // 3、进行履约调度
            List<OrderFulfillDO> orderFulfills = fulfillData.getOrderFulFills();
            Map<String, List<OrderFulfillItemDO>> orderFulfillItemMap = fulfillData.getOrderFulFillItems()
                    .stream()
                    .collect(Collectors.groupingBy(OrderFulfillItemDO::getFulfillId));
            orderFulfills.stream()
                    // 过滤出一般履约单
                    .filter(e -> OrderFulfillTypeEnum.NORMAL.equals(OrderFulfillTypeEnum.getByCode(e.getOrderFulfillType())))
                    .forEach(orderFulfill -> {
                        List<OrderFulfillItemDO> orderFulfillItems = orderFulfillItemMap.get(orderFulfill.getFulfillId());
                        // 执行履约单调度
                        fulfillScheduleService.doSchedule(orderFulfill, orderFulfillItems);
                    });
        } finally {
            redisLock.unlock(key);
        }
    }

    /**
     * 创建履约单
     * @author xuyou
     * 2023-02-12 22:04
     * @param triggerFulFillParam
    */
    private FulfillData createFulfillOrders(TriggerFulFillParam triggerFulFillParam) {
        Integer orderType = triggerFulFillParam.getOrderType();
        List<TriggerFulFillItemParam> triggerFulFillItems = triggerFulFillParam.getTriggerFulFillItems();
        // 1、创建履约单
        FulfillData fulfillData = new FulfillData();
        // 如果是预售订单,并且存在订单行,则需要进行拆单
        if (shouldSplit(orderType, triggerFulFillItems)) {
            // 进行履约单拆分
            doCreateSplitFulfillOrder(triggerFulFillParam, triggerFulFillItems, fulfillData);
        } else {
            // 直接创建履约单
            doCreateFulfillOrder(triggerFulFillParam, triggerFulFillItems, fulfillData);
        }
        // 2、设置履约单状态为"已创建"
        fulfillData.getOrderFulFills().forEach(orderFulfill ->
                orderFulfill.setStatus(OrderFulfillStatusEnum.FULFILL.getCode()));

        // 3、保存履约单、履约条目、履约单状态变更
        orderFulfillDAO.saveBatch(fulfillData.getOrderFulFills());
        orderFulfillItemDAO.saveBatch(fulfillData.getOrderFulFillItems());
        orderFulfillLogDAO.saveBatch(fulfillData.getOrderFulfillLogs());
        // 4、返回创建好的履约单
        return fulfillData;
    }


    /**
     * 进行履约单拆分
     * @author xuyou
     * 2023-02-09 21:45
     * @param triggerFulFillParam
     * @param triggerFulFillItems
     * @param fulfillData
    */
    private void doCreateSplitFulfillOrder(TriggerFulFillParam triggerFulFillParam,
                                           List<TriggerFulFillItemParam> triggerFulFillItems,
                                           FulfillData fulfillData) {
        // 剩余运费
        Integer remainDeliveryAmount = triggerFulFillParam.getDeliveryAmount();
        // 总运费
        Integer totalDeliveryAmount = triggerFulFillParam.getDeliveryAmount();
        // 付款总金额
        Integer totalPayAmount = triggerFulFillParam.getPayAmount();
        int size = triggerFulFillItems.size();
        boolean isLast;
        for (int i = 0; i < size; i++) {
            isLast = (i == size - 1);
            TriggerFulFillItemParam triggerFulFillItem = triggerFulFillItems.get(i);
            double deliveryAmount = 0.0;
            if (!isLast) {
                // 计算拆分履约单后的运费 = 付款金额 / 付款总金额 * 总运费
                deliveryAmount = Math.ceil(triggerFulFillItem.getPayAmount() / totalPayAmount.doubleValue() * totalDeliveryAmount);
                // 剩余运费
                remainDeliveryAmount -= (int) deliveryAmount;
            }
            // 通过ReceiveOrderItemParam构建履约单
            OrderFulfillDO orderFulfillDO = buildOrderFulfill(triggerFulFillItem, triggerFulFillParam,
                    !isLast ? (int) deliveryAmount : remainDeliveryAmount);
            OrderFulfillItemDO orderFulfillItemDO = FulfillApiMapstruct.INSTANCE.convertOrderFulfillItemDO(triggerFulFillItem);
            orderFulfillItemDO.setFulfillId(orderFulfillDO.getFulfillId());
            // 构建履约单操作日志
            OrderFulfillLogDO orderFulfillLogDO = OrderFulfillOperateLogUtil.build(
                    orderFulfillDO, OrderFulfillOperateTypeEnum.NEW_ORDER);
            fulfillData.addOrderFulfill(orderFulfillDO);
            fulfillData.addOrderFulFillItem(orderFulfillItemDO);
            fulfillData.addOrderFulFillLog(orderFulfillLogDO);
        }
    }


    private OrderFulfillDO buildOrderFulfill(TriggerFulFillItemParam triggerFulFillItem,
                                             TriggerFulFillParam triggerFulFillParam, Integer deliveryAmount) {
        Integer orderType = triggerFulFillParam.getOrderType();
        Integer fulfillType = OrderFulfillTypeEnum.getFulfillTypeByOrderType(orderType);
        OrderFulfillDO orderFulfillDO = FulfillApiMapstruct.INSTANCE.convertOrderFulfillDO(triggerFulFillParam);
        // 1、生成履约单ID
        orderFulfillDO.setFulfillId(genFulfillId());
        orderFulfillDO.setOrderFulfillType(fulfillType);
        orderFulfillDO.setPayAmount(triggerFulFillItem.getPayAmount());
        orderFulfillDO.setTotalAmount(triggerFulFillItem.getOriginAmount());
        orderFulfillDO.setDeliveryAmount(deliveryAmount);
        //设置扩展字段：目前仅限预售单
        orderFulfillDO.setExtJson(triggerFulFillItem.getExtJson());
        return orderFulfillDO;
    }

    /**
     * 直接创建履约单
     * @author xuyou
     * 2023-02-09 21:46
     * @param triggerFulFillParam
     * @param receiveOrderItems
     * @param fulfillData
    */
    private void doCreateFulfillOrder(TriggerFulFillParam triggerFulFillParam,
                                      List<TriggerFulFillItemParam> receiveOrderItems,
                                      FulfillData fulfillData) {
        // 生成履约单ID
        String fulfillId = genFulfillId();

        // 构建主表
        OrderFulfillDO orderFulfillDO = FulfillApiMapstruct.INSTANCE.convertOrderFulfillDO(triggerFulFillParam);
        orderFulfillDO.setFulfillId(fulfillId);
        // 履约单类型
        Integer fulfillType = OrderFulfillTypeEnum.getFulfillTypeByOrderType(triggerFulFillParam.getOrderType());
        orderFulfillDO.setOrderFulfillType(fulfillType);
        // 设置预售履约单预售商品信息
        ProductTypeEnum productType = ProductTypeEnum.PRE_SALE;
        // 过滤出商品类型为预售商品的订单行明细
        TriggerFulFillItemParam preSaleOrderItem = receiveOrderItems.stream()
                .filter(item -> productType.getCode().equals(item.getProductType())).findFirst()
                .orElse(null);
        if (null != preSaleOrderItem) {
            orderFulfillDO.setExtJson(preSaleOrderItem.getExtJson());
        }

        // 构建明细表
        List<OrderFulfillItemDO> orderFulfillItemDOList = FulfillApiMapstruct.INSTANCE.convertOrderFulfillItemDOList(receiveOrderItems);
        // 设置履约单ID
        for (OrderFulfillItemDO item : orderFulfillItemDOList) {
            item.setFulfillId(fulfillId);
        }

        // 构建履约单操作日志
        OrderFulfillLogDO orderFulfillLogDO = OrderFulfillOperateLogUtil.build(
                orderFulfillDO, OrderFulfillOperateTypeEnum.NEW_ORDER);

        fulfillData.addOrderFulfill(orderFulfillDO);
        fulfillData.addOrderFulFillItems(orderFulfillItemDOList);
        fulfillData.addOrderFulFillLog(orderFulfillLogDO);
    }


    @Override
    public void cancelFulfill(CancelFulfillParam param) {
        log.info("取消履约：request={}", JSONObject.toJSONString(param));
        String orderId = param.getOrderId();

        // 1、查询对应的履约单
        List<OrderFulfillDO> orderFulfills = orderFulfillDAO.listByOrderId(orderId);
        if (CollectionUtils.isEmpty(orderFulfills)) {
            return;
        }

        // 2、判断履约单是否可以履约
        for (OrderFulfillDO orderFulfill : orderFulfills) {
            if (OrderFulfillStatusEnum.notCancelStatus().contains(orderFulfill.getStatus())) {
                log.info("订单无法取消履约 orderId={}", orderId);
                return;
            }
        }

        // 3、取消履约
        List<String> fulfillIds = orderFulfills.stream().map(OrderFulfillDO::getFulfillId).collect(Collectors.toList());
        orderFulfillDAO.batchUpdateStatus(fulfillIds, OrderFulfillStatusEnum.FULFILL.getCode()
                , OrderFulfillStatusEnum.CANCELLED.getCode());

        // 4、添加履约单变更记录
        List<OrderFulfillLogDO> logs = new ArrayList<>(orderFulfills.size());
            orderFulfills.forEach(orderFulfill -> logs.add(
                    OrderFulfillOperateLogUtil.build(orderFulfill,OrderFulfillOperateTypeEnum.CANCEL_ORDER)));
        orderFulfillLogDAO.saveBatch(logs);
    }


    @Override
    public void triggerOrderWmsShipEvent(TriggerOrderAfterFulfillEventParam param) {
        log.info("触发订单物流配送结果事件，request={}", JSONObject.toJSONString(param));
        // 1.获取处理器
        OrderStatusChangeEnum orderStatusChange = param.getOrderStatusChange();
        OrderAfterFulfillEventProcessor wmsShipEventProcessor = wmsShipEventProcessorFactory.getWmsShipEventProcessor(orderStatusChange);
        if (wmsShipEventProcessor != null) {
            // 2.获取履约单
            OrderFulfillDO orderFulfillDO = orderFulfillDAO.getOne(param.getFulfillId());
            // 3. 执行
            wmsShipEventProcessor.execute(param, orderFulfillDO);
        }
    }


    /**
     * 校验订单是否履约过
     * @author xuyou
     * 2022/8/15 0015 23:13
     * @param orderId
     * @return boolean
     */
    private boolean orderFulfilled(String orderId) {
        return CollectionUtils.isNotEmpty(orderFulfillDAO.listByOrderId(orderId));
    }


    /**
     * 判断是否需要拆单
     */
    private boolean shouldSplit(Integer orderType, List<TriggerFulFillItemParam> triggerFulFillItems) {
        return OrderTypeEnum.PRE_SALE.getCode().equals(orderType) && triggerFulFillItems.size() > 1;
    }


    /**
     * 生成履约单id
     * @return
     */
    private String genFulfillId() {
        return RandomUtil.genRandomNumber(10);
    }
}
